package com.mys.maggie.redis.api.config;

import com.mys.maggie.redis.api.util.RedisHelpUtil;
import io.lettuce.core.cluster.ClusterClientOptions;
import io.lettuce.core.cluster.ClusterTopologyRefreshOptions;
import io.lettuce.core.resource.ClientResources;
import io.lettuce.core.resource.DefaultClientResources;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;

import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.mys.maggie.redis.api.util.RedisHelpUtil.swapIfPresentIsNull;

/**
 * desc: 根据配置文件，使用lettuce 加载redis实例
 * date:2020/9/24 14:16
 */
public class LettuceConfigService {
    private static ClientResources clientResources = DefaultClientResources.create();


    /**
     * 1、解析配置文件
     * 2、初始化 构建 JedisPoolConfig，RedisClusterConfiguration，JedisConnectionFactory，RedisTemplate
     * 3、把结果放在 map里面
     * @param properties 参数
     */
    static Map<String, RedisTemplate<String, Object>> buildLettuceTemplateInstance(List<MagRedisProperties.Cluster> clusters
            , MagRedisProperties properties){
        Map<String, RedisTemplate<String, Object>> redisTemplateMap = new HashMap<>();

        // Initialize multi cluster
        clusters.forEach(cluster -> {
            //1、构建lettuce的 GenericObjectPoolConfig 连接池
            GenericObjectPoolConfig poolConfig = buildLettucePoolConfig(cluster,properties);
            //2、设置集群刷新策略
            ClusterTopologyRefreshOptions refreshOptions = buildRedisClusterRefreshOptions();
            //3、根据连接池和集群配置信息构建连接工厂
            RedisConnectionFactory connectionFactory = buildLettuceConnectionFactory(poolConfig,refreshOptions,cluster,properties);
            //4、构造redisTemplate
            RedisTemplate<String, Object> redisTemplate = RedisHelpUtil.buildRedisTemplate(connectionFactory,cluster,properties);
            //5、放置map
            redisTemplateMap.put(cluster.getName(),redisTemplate);
        });

        return redisTemplateMap;
    }


    /**
     * 根据参数构建lettuce连接池
     * @param cluster 配置文件
     * @return JedisPoolConfig
     */
    private static GenericObjectPoolConfig buildLettucePoolConfig(MagRedisProperties.Cluster cluster, MagRedisProperties properties){
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        Integer maxTotal = swapIfPresentIsNull(cluster.getMaxActive(),properties.getMaxActive() );
        Integer maxIdle = swapIfPresentIsNull(cluster.getMaxIdle(),properties.getMaxIdle() );
        Integer minIdle = swapIfPresentIsNull(cluster.getMinIdle(),properties.getMinIdle() );
        Integer maxWaitMillis = swapIfPresentIsNull(cluster.getMaxWait(),properties.getMaxWait() );

        //接池中最大连接数。高版本：maxTotal，低版本：maxActive
        poolConfig.setMaxTotal(maxTotal);
        //连接池中最少空闲的连接数
        poolConfig.setMinIdle( minIdle );
        //连接池中最大空闲的连接数
        poolConfig.setMaxIdle(maxIdle);
        //当连接池资源耗尽时，调用者最大阻塞的时间，超时将跑出异常。单位，毫秒数;默认为-1.表示永不超时。高版本：maxWaitMillis，低版本：maxWait
        poolConfig.setMaxWaitMillis( maxWaitMillis );

        return poolConfig;
    }


    /**
     * 构造集群刷新策略
     * ClusterTopologyRefreshOptions.RefreshTrigger 触发器一共4种
     * @return refreshOptions
     */
    private static ClusterTopologyRefreshOptions buildRedisClusterRefreshOptions(){
        return  ClusterTopologyRefreshOptions.builder()
//                .enableAdaptiveRefreshTrigger(ClusterTopologyRefreshOptions.RefreshTrigger.MOVED_REDIRECT, ClusterTopologyRefreshOptions.RefreshTrigger.PERSISTENT_RECONNECTS)
                .enableAllAdaptiveRefreshTriggers() //开启所有触发器。
                .adaptiveRefreshTriggersTimeout(Duration.ofSeconds(10L))  //刷新10秒超时。默认30秒
                .enablePeriodicRefresh(Duration.ofSeconds(15L)).build(); //15秒拉取一次集群信息。默认60秒
    }

    /**
     * 构建lettuce 连接池
     * @param poolConfig 链接配置
     * @param refreshOptions 刷新策略
     * @param cluster 集群详细配置
     * @param properties redis配置文件
     * @return RedisConnectionFactory
     */
    private static RedisConnectionFactory buildLettuceConnectionFactory(GenericObjectPoolConfig poolConfig,
                                                                        ClusterTopologyRefreshOptions refreshOptions,
                                                                        MagRedisProperties.Cluster cluster,
                                                                        MagRedisProperties properties){
        //读取数据超时时间
        Integer readTimeout = swapIfPresentIsNull(cluster.getReadTimeOut(),properties.getReadTimeOut() );
        List<RedisNode> nodeList = RedisHelpUtil.convertRedisNodeList( cluster.getNodes() );
        String password = cluster.getPassword();
        //集群配置
        RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
        redisClusterConfiguration.setClusterNodes( nodeList );
        if (StringUtils.isNotEmpty(password)) {
            redisClusterConfiguration.setPassword(  RedisPassword.of( password ) );
        }
        //lettuce 连接池配置
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder =
                                LettucePoolingClientConfiguration.builder()
                                        .poolConfig( poolConfig )
                                        .clientOptions( ClusterClientOptions.builder().topologyRefreshOptions( refreshOptions ).build());
        builder.clientResources( clientResources );//不为空
        if (readTimeout != null && readTimeout>0 ){
            builder.commandTimeout(Duration.ofMillis((long)readTimeout)); //默认60秒
        }
//        builder.clientName(properties.getApplicationName()+"_"+cluster.getName());//客户端名称

        //构建lettuce连接池
        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisClusterConfiguration, builder.build());
        factory.afterPropertiesSet();
        return factory;
    }





}
